#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include "Util.h"

void runWorkload(void* );

void preAllocate(int fileDescriptor, long length);
typedef enum {PREALLOCATE, NORMAL} workLoadType_t;

struct workLoadParams {
	int fileDescriptor;
	float fsyncToWriteRatio;
	int blockSize;
	int numWrites;
};

struct workLoadParams* constructWorkLoadParams(int fileDescriptor, float fsyncToWriteRatio, int blockSize, int numWrites);

int main(int argc, char *argv[]) {
	int numWrites = 0, blockSize = 0, fileDescriptor = 0 ;
	float fsyncToWriteRatio = 0.0f;
	char* fileName;
	struct timespec startTime, endTime;
	double timeForWorkload = 0;

	validateAndFillInputArgumentForFSAllocations(argc, argv, &numWrites,
			"NumWrites", &blockSize, "BlockSize", &fileName, "FileName", &fsyncToWriteRatio, "fsyncToWriteRatio");

	fileDescriptor = open(fileName, O_CREAT | O_WRONLY | O_TRUNC);
	if (fileDescriptor == -1) {
		printf("Unable to open file %s\n", fileName);
		perror("Checking the error");
		exit(-1);
	}

	struct workLoadParams* param = constructWorkLoadParams(fileDescriptor, fsyncToWriteRatio, blockSize, numWrites);

	clock_gettime(CLOCK_REALTIME, &startTime);
	runWorkload(param);
	clock_gettime(CLOCK_REALTIME, &endTime);
	timeForWorkload = getDiffInNanoSeconds(startTime, endTime);
	timeForWorkload /= 10e6;

	printf("%f %f %d\n", fsyncToWriteRatio, (double)timeForWorkload/(double)numWrites, numWrites);

	close(fileDescriptor);
	return EXIT_SUCCESS;
}

struct workLoadParams* constructWorkLoadParams(int fileDescriptor, float fsyncToWriteRatio, int blockSize, int numWrites) {
	struct workLoadParams* param = (struct workLoadParams*) malloc(sizeof(struct workLoadParams));

	param->fileDescriptor = fileDescriptor;
	param->blockSize = blockSize;
	param->numWrites = numWrites;
	param->fsyncToWriteRatio = fsyncToWriteRatio;

	return param;
}

void preAllocate(int fileDescriptor, long length) {
	int fallocateResult = 0, fsyncResult = 0;

	fallocateResult = fallocate(fileDescriptor, 0, 0, length);
	if (fallocateResult == -1) {
		printf("Error while doing fallocate for fd  %d\n", fileDescriptor);
		perror("ERROR During fallocate");
		exit(-1);
	}
	fsyncResult = fsync(fileDescriptor);
	if (fsyncResult == -1) {
		perror("Fsync Error");
		exit(-1);
	}
}


void runWorkload(void* paramPtr) {
	struct workLoadParams* param = (struct workLoadParams*)paramPtr;
	int fileDescriptor = param->fileDescriptor, blockSize = param->blockSize, numWrites = param->numWrites;
	int counter = 0, writeResult = 0, fsyncResult = 0;
	struct timespec startTime, endTime;
	double timeForWrite = 0;
	//char *logBuffer = (char*) malloc(sizeof(char) * 100000);
	char buf[30], dataBuffer[blockSize];
	int fsyncInterval = (double)( param->fsyncToWriteRatio * numWrites ) / (double) 100.0;
	//printf("fsyncInterval is %d\n", fsyncInterval);

	for (counter = 0; counter < numWrites; counter++) {
		fillAllocatedMemory(dataBuffer, blockSize);
		//clock_gettime(CLOCK_REALTIME, &startTime);

		writeResult = write(fileDescriptor, dataBuffer, blockSize);
		if (writeResult == -1) {
			perror("Write Error");
			exit(-1);
		}

		if (fsyncInterval !=0 && counter % fsyncInterval == 0) {
			//sprintf(buf, "Doing fsync at counter %d!\n", counter);
			//strcat(logBuffer, buf);
			fsyncResult = fsync(fileDescriptor);
			if (fsyncResult == -1) {
				perror("Fsync Error");
				exit(-1);
			}
		}

		/*
		clock_gettime(CLOCK_REALTIME, &endTime);
		timeForWrite = getDiffInNanoSeconds(startTime, endTime);
		timeForWrite /= 10e6;
		sprintf(buf, "%d %f\n", counter, timeForWrite);
		strcat(logBuffer, buf);
		*/
	}

	fsyncResult = fsync(fileDescriptor);
	if (fsyncResult == -1) {
		perror("Fsync Error");
		exit(-1);
	}

	//puts(logBuffer);
	return;
}
